package org.salatec.tapestry4e.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.salatec.tapestry4e.TMLPlugin;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * This class manages the live store of T5Components information.
 * 
 * @author Juan M. Salamanca
 * 
 *         06/03/2009
 */
public class T5ComponentsStore {

	private static final String FILE_NAME = "T5components.xml";

	private File storeFile;

	private List<T5Component> components;

	private List<String> applicationPackages;

	public List<String> getApplicationPackages() {
		return applicationPackages;
	}

	/**
	 * Finds the path of the store file for the given project, and creates the
	 * file if it does not exist. Finally, initialises the components array.
	 * 
	 * @param project
	 * @throws IOException
	 */
	public T5ComponentsStore(IProject project) {
		IPath pathToStore = project.getWorkingLocation(TMLPlugin.ID);
		File pathFile = pathToStore.toFile();
		storeFile = new File(pathFile.getAbsolutePath() + File.separator
				+ FILE_NAME);
		try {
			if (!storeFile.exists()) {
				storeFile.createNewFile();
				components = new ArrayList<T5Component>();
			} else {

				parseResources();

			}
		} catch (Exception e) {
			components = new ArrayList<T5Component>();
			Logger.log(Logger.WARNING, "Error parsing component store", e);
		}
	}

	private void parseResources() throws Exception {
		components = new ArrayList<T5Component>();
		applicationPackages = new ArrayList<String>();

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = builder.parse(new FileInputStream(this.storeFile));
		Element root = doc.getDocumentElement();
		NodeList packages = root.getElementsByTagName("app-package");
		for (int l = 0; l < packages.getLength(); l++) {
			this.applicationPackages.add(((Element) packages.item(l))
					.getFirstChild().getNodeValue());
		}
		NodeList componentList = root.getChildNodes();
		for (int i = 0; i < componentList.getLength(); i++) {
			Node node = componentList.item(i);
			if (node instanceof Element) {
				Element component = (Element) componentList.item(i);
				T5Component t5c = new T5Component();

				t5c.setName(component.getAttribute("name"));
				if (!"".equalsIgnoreCase(t5c.getName())) {
					NodeList parameters = component
							.getElementsByTagName("parameter");
					List<String> t5pars = new ArrayList<String>();
					for (int j = 0; j < parameters.getLength(); j++) {
						t5pars.add(((Element) parameters.item(j))
								.getFirstChild().getNodeValue());
					}
					Collections.sort(t5pars);
					t5c.setParameters(t5pars);
					NodeList properties = component
							.getElementsByTagName("property");
					List<String> t5props = new ArrayList<String>();
					for (int k = 0; k < properties.getLength(); k++) {
						t5props.add(((Element) properties.item(k))
								.getFirstChild().getNodeValue());
					}
					t5c.setProperties(t5props);

					components.add(t5c);
				}
			}
		}

		Collections.sort(components);

	}

	public void addApplicationPackage(String packageName) {
		if (applicationPackages == null) {
			applicationPackages = new ArrayList<String>();
		}
		applicationPackages.add(packageName);
	}

	public List<T5Component> getComponents() {
		return components;
	}

	/**
	 * Adds a component to the array.
	 * 
	 * @param cls
	 * @throws ClassNotFoundException
	 * @throws JavaModelException
	 */

	public void add(ICompilationUnit unit) throws JavaModelException {
		addOrUpdate(unit);
		StoreWorker worker = new StoreWorker();
		worker.start();

	}

	private void addOrUpdate(ICompilationUnit unit) throws JavaModelException {

		T5Component component = new T5Component(unit);
		if (component.getName() != null
				&& !"".equalsIgnoreCase(component.getName())) {
			if (components.contains(component))
				components.set(components.indexOf(component), component);
			else
				components.add(component);
		}
	}

	public void add(List<ICompilationUnit> classNames)
			throws JavaModelException {
		Iterator<ICompilationUnit> it = classNames.iterator();
		while (it.hasNext()) {
			addOrUpdate(it.next());
		}
		StoreWorker worker = new StoreWorker();
		worker.start();

	}

	public void remove(ICompilationUnit unit) throws JavaModelException {
		T5Component component = new T5Component(unit);
		components.remove(components.indexOf(component));
		StoreWorker worker = new StoreWorker();
		worker.start();
	}

	private class StoreWorker extends Thread {

		public void run() {
			try {
				store();
			} catch (IOException e) {
				Logger.log(Logger.WARNING, "Error Storing Components in File: "
						+ storeFile.getAbsolutePath());
			}
		}

		/**
		 * Stores the results in the storeFile.
		 * 
		 * @throws IOException
		 */
		private void store() throws IOException {
			FileWriter writer = new FileWriter(storeFile);
			writer.write("<Components>");
			if (applicationPackages != null) {
				Iterator<String> pit = applicationPackages.iterator();
				while (pit.hasNext()) {
					writer.append("<app-package>" + pit.next()
							+ "</app-package>");
				}
			}
			Iterator<T5Component> it = components.iterator();
			while (it.hasNext()) {
				String componentStr = it.next().toString();
				if (componentStr != null)
					writer.append(componentStr);
			}
			writer.append("</Components>");
			writer.close();
		}

	}

}
